#pragma once

// --------------------------------------------------------------------------------------------
class SimpleEffect
{
public:
    VulkanEffect* m_effect = nullptr;
    VulkanEffect* m_effBackDraw = nullptr;
    VulkanEffect* m_effBackDraw2 = nullptr;

public:
    static void _addEffect(VulkanEffect* effect, const String& file, VulkanShaderType type, VulkanShaderLanguage lang)
    {
        if (file.empty())
            return;

        VulkanSharedPtr<VulkanShader> shader(g_simpleEngine.m_vkDevice->createShader());
        shader->create(type, lang, _readMediaFileData(file).c_str());
        effect->addShader(shader);
    }

    static VulkanEffect* _createEffect(const String& vsFile, const String& gsFile, const String& psFile, VulkanShaderLanguage lang)
    {
        VulkanEffect* effect = g_simpleEngine.m_vkDevice->createEffect();

        _addEffect(effect, vsFile, VulkanShaderType::Vertex, lang);
        _addEffect(effect, gsFile, VulkanShaderType::Geometry, lang);
        _addEffect(effect, psFile, VulkanShaderType::Fragment, lang);

        effect->complete();
        return effect;
    }

    void create()
    {
        m_effBackDraw = _createEffect("back_hlsl.vs", "", "back_hlsl.ps", VulkanShaderLanguage::HLSL);
        m_effect = _createEffect("demoTri_glsl.vert", "", "demoTri_glsl.frag", VulkanShaderLanguage::GLSL);
        m_effBackDraw2 = _createEffect("back_hlsl.vs", "", "back2_hlsl.ps", VulkanShaderLanguage::HLSL);
    }
} g_simpleEffect;


// --------------------------------------------------------------------------------------------
class SimpleConstBuffer
{
public:
    struct UniformBufferObject
    {
        glm::mat4 model;
        glm::mat4 view;
        glm::mat4 proj;
    };

#pragma warning(push)
#pragma warning(disable: 4324) // structure was padded due to alignment specifier
    struct InfoMiscBuffer
    {
        glm::vec2 info1;
        alignas(16) glm::vec2 info2;
        alignas(16) glm::vec4 info0;
    };
#pragma warning(pop)

    struct BackInfo
    {
        glm::vec4 g_param0;
        glm::vec4 g_param1;
    };

    VulkanConstBuffer* m_uniformBuffer = nullptr;
    VulkanConstBuffer* m_cbInfoMisc = nullptr;
    VulkanConstBuffer* m_cbBackInfo = nullptr;

public:
    static VulkanConstBuffer* _createBuf(uint32_t sz)
    {
        VulkanConstBuffer* buf = g_simpleEngine.m_vkDevice->createConstBuffer();
        buf->create(sz);
        return buf;
    }

    void create()
    {
        m_uniformBuffer = _createBuf(sizeof(UniformBufferObject));
        m_cbInfoMisc = _createBuf(sizeof(InfoMiscBuffer));
        m_cbBackInfo = _createBuf(sizeof(BackInfo));
    }

    void destroy()
    {
        VulkanSharedBase::safeRlease(m_cbBackInfo);
        VulkanSharedBase::safeRlease(m_cbInfoMisc);
        VulkanSharedBase::safeRlease(m_uniformBuffer);
    }

    static void calcCurrentMatrix(float currentTimeSec, UniformBufferObject& ubo, int objectIdx)
    {
        khaosStaticAssert(TEST_RENDER_OBJ_COUNT > 1);
        static const int cntSideWidth = (int)sqrt(TEST_RENDER_OBJ_COUNT);
        static const int cntSideHeight = (TEST_RENDER_OBJ_COUNT + cntSideWidth - 1) / cntSideWidth;
        static const float gridSize = 2.0f;
        static const float startX = gridSize * cntSideWidth * -0.5f;
        static const float startY = gridSize * cntSideHeight * 0.5f;

        // rotate
        float timeScale = (objectIdx % 10) / 9.f * glm::radians(360.0f);
        timeScale += fmodf(currentTimeSec, glm::radians(360.0f));
        glm::mat4x4 matRot = glm::rotate(glm::identity<glm::mat4x4>(), timeScale, glm::vec3(0.0f, 0.0f, 1.0f));

        // offset
        int row = objectIdx / cntSideWidth;
        int col = objectIdx - row * cntSideWidth;

        glm::vec3 off;
        off.x = startX + col * gridSize;
        off.y = startY - row * gridSize;
        off.z = 0;

        glm::mat4x4 matTrans = glm::translate(glm::identity<glm::mat4x4>(), off);

        // model
        ubo.model = matTrans * matRot;
    }

    static void calcCurrentMisc(float /*currentTimeSec*/, InfoMiscBuffer& infoMisc, int objectIdx)
    {
        // info misc
        infoMisc.info0 = glm::vec4(1, 1, 1, 1);
        infoMisc.info0.z = (objectIdx % 255) / 255.0f;
        infoMisc.info1 = glm::vec2(0, 0);
        infoMisc.info2 = glm::vec2(0, 0);
    }

} g_simpleConstBuffer;

